package org.nativejpa.support.dao;

import org.nativejpa.query.QueryExt;
import org.nativejpa.mapping.EntityFactory;
import org.nativejpa.EntityManagerExt;

import javax.persistence.Query;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

/**
 * A DAO support class to provide useable help methods
 *
 * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
 * @create 2009-4-9 13:52:03
 */
public abstract class DAOSupport<T> {

    /**
     * 返回 EntityManager，由子类使用 @PersistenceContext 注入
     */
    protected abstract EntityManager getEntityManager();

    protected abstract Class<T> getEntityClass();

    /**
     * 根据 Entity Class生成 Entity对象
     *
     */
    public T newEntityObject() {
        return newEntityObject(new HashMap<String,Object>());
    }

    /**
     * 使用数据 Map，生成 Entity对象，Map的key和Entity的Column对应
     *
     * @param dataMap data dataMap
     */
    public T newEntityObject(final Map<String, Object> dataMap) {
        return EntityFactory.newEntityObject(getEntityClass(), dataMap);
    }

    /**
     * create NamedNativeQeury based on given namedQueryName
     * @param nativeQueryName query name
     */
    public QueryExt createNamedNativeQuery(String nativeQueryName) {
        return (QueryExt)getEntityManager().createNamedQuery(nativeQueryName);
    }

    public QueryExt createUnnamedNativeQuery(String sql, Class<?> resultClass){
        String tempName = resultClass.getName() + "_" + sql;
        return ((EntityManagerExt)getEntityManager()).createTempNamedNativeQuery(tempName, sql, resultClass, "", null);
    }

    /**
     *
     * @param sql
     * @param resultClass
     * @param cacheName should be specified correcly, otherwise, the cache may be confused!!!
     * @return
     */
    public QueryExt createUnnamedNativeQuery(String sql, Class<?> resultClass, String cacheName){
        String tempName = resultClass.getName() + "_" + sql;
        return ((EntityManagerExt)getEntityManager()).createTempNamedNativeQuery(tempName, sql, resultClass, "", cacheName);
    }

    public QueryExt createUnnamedNativeQuery(String sql, String resultsetMapping, String cacheName){
        String tempName = void.class.getName() + "_" + sql; //use void.class
        return ((EntityManagerExt)getEntityManager()).createTempNamedNativeQuery(tempName, sql, void.class, resultsetMapping, cacheName);
    }

    public List<T> executeNamedNativeQuery(String namedQuery, Map<String, ?> paramMap) {
        return (List<T>)executeNamedNativeQuery(namedQuery, paramMap, 0, Integer.MAX_VALUE);
    }

    /**
     * 使用一个预定义的 query 语句进行查询，返回 entity
     *
     * @param namedQuery   named query
     * @param paramMap     parameter map
     * @param startPostion 第一个值的位置
     * @param maxResult    取值范围
     * @return 返回符合需要的 entity list
     */
    public List<T> executeNamedNativeQuery(String namedQuery, Map<String, ?> paramMap, int startPostion, int maxResult) {
        Query query = createNamedNativeQuery(namedQuery);
        if (paramMap != null) {
            for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult(startPostion);
        query.setMaxResults(maxResult);
        return (List<T>)query.getResultList();
    }

    public int executeNamedNativeUpdateQuery(String namedQuery, Map<String, ?> paramMap) {
        Query query = createNamedNativeQuery(namedQuery);
        if (paramMap != null) {
            for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.executeUpdate();
    }

    public List<T> executeNativeQuery(Query query, Map<String, ?> paramMap) {
        return executeNativeQuery(query, paramMap, 0, Integer.MAX_VALUE);
    }

    public Object executeNativeQuerySingleResult(Query query, Map<String, ?> paramMap) {
        if (paramMap != null) {
            for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.getSingleResult();
    }

    public List<T> executeNativeQuery(Query query, Map<String, ?> paramMap, int startPosition, int maxResult) {
        if (paramMap != null) {
            for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult(startPosition);
        query.setMaxResults(maxResult);
        return (List<T>)query.getResultList();
    }

    public int executeNativeUpdateQuery(Query query, Map<String, ?> paramMap) {
        if (paramMap != null) {
            for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query.executeUpdate();
    }
}
